'''
Contains the CSNClaim class, which is used to create a new CSN warranty claim,
and submit all Columbus City Schools CSN claims where and all parts have shipped.
'''

import re
from datetime import datetime
from dateutil.relativedelta import relativedelta
import PySimpleGUI as sg
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import (
    TimeoutException,
    NoSuchFrameException,
    NoSuchElementException,
    ElementClickInterceptedException,
)
from pyperclip import copy as pyperclip_copy, PyperclipWindowsException
from warranty_manager.claim.claim import Claim


class CSNClaim(Claim):
    '''
    Creates a new CSN warranty claim, or submits all Columbus City Schools
    CSN claims where all parts have shipped.
    '''
    def __init__(self, info):
        super().__init__(info)
        self.__today = datetime.today()
        self.__yesterday = self.__today - relativedelta(days=1)
        self.__messages = [
            'response time not available',
            'Mandatory on Notes Validation',
            'Engineer Is Qualified',
            'Order Validation Successful.{1} {2}',
            'CSN is validating your order.',
            'Price and ETA may change on submit',
            'Check later for order info',
            'Unable to performed RoHS compliance check.',
        ]
        self.__is_last_page = False

    def __check_claim_info_loaded(self):
        '''
        Determines when the progress bar for CSN claim operations appears
        and disappears from the DOM.
        '''
        try:
            WebDriverWait(self._browser, 3).until(
                EC.visibility_of_any_elements_located(
                    (By.CLASS_NAME, 'ProgressBar')
                )
            )
        except TimeoutException:
            return False

        try:
            return self._wait.until(
                EC.invisibility_of_element_located(
                    (By.CLASS_NAME, 'ProgressBar')
                )
            )
        except TimeoutException:
            self._browser.quit()
            sg.popup_error(
                'HP website appears to have stopped responding;\n'
                'the program will need to be re-run.',
                title='Error Message'
            )

    def __close_message_of_the_day(self):
        '''
        Closes the message of the day window, which appears the first time
        a user logs into CSN on a given day.
        '''
        try:
            self._browser.execute_script(
                'arguments[0].click();',
                WebDriverWait(self._browser, 5).until(
                    EC.element_to_be_clickable(
                        (By.CSS_SELECTOR, '#motdpopuplight > a')
                    )
                )
            )
        except TimeoutException:
            print('The message of the day was not found.')

    def __go_to_service_delivery_page(self):
        '''
        Clicks the link on the CSN home page that goes to the page where a new
        CSN claim can be created, or any CSN claims can be submitted.
        '''
        self._accept_cookies()

        self._browser.execute_script(
            'arguments[0].click();',
            self._wait.until(
                EC.element_to_be_clickable(
                    (By.LINK_TEXT, 'HP service delivery')
                )
            )
        )

        self.__close_message_of_the_day()

    def __open_new_claim_window(self):
        '''
        Clicks the link on the CSN Service Delivery page that opens a new
        claim window.
        '''
        self._browser.execute_script(
            "arguments[0].click();",
            self._wait.until(
                EC.element_to_be_clickable(
                    (
                        By.LINK_TEXT,
                        "Create warranty exchange in Global Service Delivery"
                    )
                )
            )
        )

        self._wait.until( EC.number_of_windows_to_be( (2) ) )
        self._browser.switch_to.window(self._browser.window_handles[1])

    def __set_csn_event_num(self):
        '''
        Updates the Claim instance with the CSN event number from the new claim.
        '''
        csn_event_elem = self._wait.until(
            EC.presence_of_element_located( (By.ID, 'tdTitle') )
        )
        event_num_match = re.search(r'XC\d{8}', csn_event_elem.text)
        self._info['event number'] = event_num_match.group(0)

    def __check_device_info_retrieved(self):
        '''
        Makes sure that all device and user information has been retrieved from HP's warranty
        database, before trying to check the status messages.
        '''
        while True:
            try:
                return WebDriverWait(self._browser, 5).until(
                    EC.text_to_be_present_in_element(
                        (By.CSS_SELECTOR, '#tdProcessStatus > b:nth-child(1)'),
                        'Configuration,Student completed successfully.'
                    )
                )
            except TimeoutException:
                continue

    def __retrieve_device_info(self):
        '''
        Clicks the button in the new CSN claim window that retrieves
        all device and user data from the HP warranty database.
        '''
        self._wait.until(
            EC.presence_of_element_located(
                (By.ID, "AreaInformation1__ctl0_retrieveInformation")
            )
        ).click()
        self.__check_device_info_retrieved()

    def __fill_in_device_and_learner_info(self):
        '''
        Fills in the data needed to determine the device's warranty eligibility,
        and whether or not the user is qualified to submit claims for it:
        the date the problem occurred with device, the HP learner ID of the user submitting
        the claim, the device serial number, and the device product number.
        '''
        sn_field = self._wait.until(
            EC.presence_of_element_located( (By.ID, "serialnumber") )
        )
        sn_field.send_keys(self._info['serial number'])

        pn_field = self._wait.until(
            EC.presence_of_element_located( (By.ID, "productnumber") )
        )
        pn_field.send_keys(self._info['product number'])

        failure_date_field = self._wait.until(
            EC.presence_of_element_located( (By.ID, "failureReportedDate") )
        )
        failure_date_field.send_keys( self.__yesterday.strftime('%m/%d/%Y') )

        learner_id_field = self._wait.until(
            EC.presence_of_element_located( (By.ID, "technicianid") )
        )
        learner_id_field.send_keys(self._info['learner id'])

        self.__retrieve_device_info()

    def __check_device_warranty(self):
        '''
        Checks for a message that the provided device is out of warranty.
        If the message appears, then the web browser is closed, and the program stops;
        if the message doesn't appear, the rest of the program starts
        to fill out all of the remaining claim information.
        '''
        try:
            WebDriverWait(self._browser, 5).until(
                EC.frame_to_be_available_and_switch_to_it(
                    (By.ID, 'dialog-body')
                )
            )
            WebDriverWait(self._browser, 5).until(
                EC.text_to_be_present_in_element(
                    (
                        By.CSS_SELECTOR,
                        '#hpForm > table > tbody > tr:nth-child(2) > '
                        'td:nth-child(2) > span'
                    ),
                    'Unit is out of warranty'
                )
            )
            self._browser.quit()
            sg.popup(
                f"Device with serial number '{self._info['serial number']}' is out of warranty.",
                title='Warranty Status Message',
            )
            return False
        except TimeoutException:
            return True

    def __switch_to_information_tab(self):
        '''Clicks on the "Information" tab, in the new CSN claim.'''
        self._browser.switch_to.default_content()

        info_tab = self._wait.until(
            EC.element_to_be_clickable( (By.ID, "navInfo") )
        )
        try:
            info_tab.click()
        except ElementClickInterceptedException:
            self._browser.execute_script("arguments[0].click();", info_tab)

    def __switch_to_status_message_frame(self):
        '''
        Changes the Chromedriver focus to the frame that contains the
        status messages.
        '''
        while True:
            try:
                self._browser.switch_to.default_content()
                self._browser.switch_to.frame('iFrame')
                self._browser.switch_to.frame('hpGrid')
                self._browser.switch_to.frame('_grid')
                break
            except NoSuchFrameException:
                continue

    def __switch_to_status_tab(self):
        '''
        Clicks on the "Status" tab, in the claim, and switches to the frame
        with its status messages.
        '''
        self.__switch_to_information_tab()

        status_tab = self._wait.until(
            EC.element_to_be_clickable( (By.ID, "StatusTab") )
        )
        try:
            status_tab.click()
        except ElementClickInterceptedException:
            self._browser.execute_script("arguments[0].click();", status_tab)

        self.__switch_to_status_message_frame()

    def __get_status_messages(self):
        '''Gets a list of all the claim's status messages.'''
        self.__switch_to_status_tab()

        while True:
            try:
                return self._browser.find_elements_by_css_selector(
                    "#gridBodyTable > tbody > tr > td:nth-child(4)"
                )
            except NoSuchElementException:
                continue

    def __check_status_messages(self):
        '''
        Makes sure that none of the claim's status messages indicate a problem
        with putting in the order.
        '''

        # TODO: Figure out a way to deal with this message
        '''
        <td style="Border-Bottom:#cccccc 1px solid;padding-left:5px;text-align:left;word-break: break-all;" width="384" class="gridPreviewOn">
            Important part number related information is available<br>
        </td>

        Selector: #gridBodyTable > tbody > tr:nth-child(1) > td:nth-child(4)
        '''

        message_elems = self.__get_status_messages()

        for message_elem in message_elems:
            message = message_elem.text

            if message not in self.__messages:
                self._browser.quit()
                sg.popup_error(
                    f'There is an unexpected message: "{message}".\n'
                    'Please check that all claim information is valid, '
                    'and try to submit it again.',
                    title='Error Message'
                )
            else:
                continue

    def __switch_to_sales_order_frame(self):
        '''
        Changes the ChromeDriver focus to the frame with the form to add
        part numbers to the claim.
        '''
        while True:
            try:
                WebDriverWait(self._browser, 1).until(
                    EC.frame_to_be_available_and_switch_to_it(
                        (By.ID, 'areaOrderGSFrame')
                    )
                )
                break
            except TimeoutException:
                continue

    def __switch_to_sales_order_tab(self):
        '''
        Clicks on the claim's "Sales Order" tab, and switches to the frame
        with the form to enter the part numbers.
        '''
        self._browser.switch_to.default_content()

        sales_order_tab = self._wait.until(
            EC.element_to_be_clickable( (By.ID, "navOrder") )
        )
        try:
            sales_order_tab.click()
        except ElementClickInterceptedException:
            self._browser.execute_script(
                "arguments[0].click();", sales_order_tab
            )

        self.__switch_to_sales_order_frame()

    def __add_parts_to_order(self):
        '''Fills in all the claim's part numbers in the "Add Parts" form.'''
        self.__switch_to_sales_order_tab()

        part_num_field = self._wait.until(
            EC.visibility_of_element_located( (By.ID, 'partnumber') )
        )
        add_part_btn = self._wait.until(
            EC.visibility_of_element_located( (By.ID, 'btnSOAddPart') )
        )

        for part_num in self._info['part numbers']:
            part_num_field.send_keys(part_num)
            add_part_btn.click()

    def __switch_to_shipping_details_tab(self):
        '''Clicks on the claim's "Shipping Details" tab.'''
        self.__switch_to_sales_order_tab()

        shipping_details_tab = self._wait.until(
            EC.element_to_be_clickable(
                (By.ID, "ShippingDetailsTab")
            )
        )
        try:
            shipping_details_tab.click()
        except ElementClickInterceptedException:
            self._browser.execute_script(
                "arguments[0].click();", shipping_details_tab
            )

    def __fill_in_shipping_info(self):
        '''
        Checks to make sure that shipping address can be filled in,
        and fills in the correct shipping address for the parts
        in the claim.

        If shipping address cannot be entered, then the web browser
        is closed, and the program stops.
        '''
        self.__switch_to_shipping_details_tab()

        dropship_radio = self._wait.until(
            EC.element_to_be_clickable( (By.ID, "isdropship1") )
        )

        if not dropship_radio.is_enabled():
            self._browser.quit()
            sg.popup_error(
                'Dropship radio is disabled, so claim will have to '
                'be re-sumbitted.\nThis is an error with the HP CSN website.',
                title='Error Message'
            )
        else:
            try:
                dropship_radio.click()
            except ElementClickInterceptedException:
                self._browser.execute_script(
                    "arguments[0].click();", dropship_radio
                )

        shipping_names = self._wait.until(
            EC.element_to_be_clickable( (By.ID, "ShiptoFriendlyNames_Input") )
        )
        shipping_names.send_keys(self._info['ship name'], Keys.ARROW_DOWN)

        po_num_elem = self._wait.until(
            EC.presence_of_element_located( (By.ID, 'purchaseordernumber') )
        )
        po_num_elem.send_keys(self._info['po number'])

    def __switch_to_claim_step_1_repair_frame(self):
        '''
        Switches the ChromeDriver focus to the frame where the CSN event
        information can be filled in: start and end date/time, and end user
        contact information.
        '''
        while True:
            try:
                WebDriverWait(self._browser, 1).until(
                    EC.frame_to_be_available_and_switch_to_it(
                        (By.ID, 'areaReturnGSFrame')
                    )
                )
                break
            except TimeoutException:
                continue

    def __switch_to_claim_step_1_repair_tab(self):
        '''Clicks on the claim's "Claim Step 1 - Repair" tab.'''
        self._browser.switch_to.default_content()

        repair_tab = self._wait.until(
            EC.element_to_be_clickable(
                (By.ID, "navReturn")
            )
        )
        try:
            repair_tab.click()
        except ElementClickInterceptedException:
            self._browser.execute_script(
                "arguments[0].click();", repair_tab
            )

        self.__switch_to_claim_step_1_repair_frame()

    def __fill_in_event_info(self):
        '''Fills in the start and end date/time for the claim.'''
        self.__switch_to_claim_step_1_repair_tab()

        # To get the event start date
        self._wait.until(
            EC.element_to_be_clickable( (By.ID, "eventstartdateGS") )
        ).send_keys( self.__today.strftime('%m/%d/%Y') )

        # To get the event start time
        self._wait.until(
            EC.element_to_be_clickable( (By.ID, "eventstarttimeGSTime_Input") )
        ).send_keys('8:00 AM')

        # To get the event end date
        self._wait.until(
            EC.element_to_be_clickable( (By.ID, "eventfinishdateGS") )
        ).send_keys( self.__today.strftime('%m/%d/%Y') )

        # To get the event end time
        self._wait.until(
            EC.element_to_be_clickable( (By.ID, "eventfinishtimeGSTime_Input") )
        ).send_keys('9:00 AM')

    def __switch_to_csn_address_book_window(self):
        '''
        Opens the CSN address book, and switches the ChromeDriver focus
        to that window.
        '''
        self._wait.until(
            EC.element_to_be_clickable( (By.ID, 'searchIcon') )
        ).click()

        self._wait.until( EC.number_of_windows_to_be( (3) ) )
        self._browser.switch_to.window(self._browser.window_handles[2])

    def __switch_from_csn_address_book_window(self):
        '''
        Switches the ChromeDriver focus from the CSN address book window,
        back to the new claim window.
        '''
        self._wait.until( EC.number_of_windows_to_be( (2) ) )
        self._browser.switch_to.window(self._browser.window_handles[1])

    def __fill_in_end_user_info(self):
        '''
        Opens the CSN address book, finds the correct customer name,
        based on the name in the claim, and then enters that customer's
        name and address into the claim's event information form.
        '''
        self.__switch_to_csn_address_book_window()

        # To look for the customer by name
        self._wait.until(
            EC.presence_of_element_located( (By.ID, 'Find') )
        ).send_keys(self._info['end user'])

        # To select the customer (by double-click)
        self._wait.until(
            EC.element_to_be_clickable( (By.ID, 'btnGo') )
        ).click()

        customer_selection = self._wait.until(
            EC.element_to_be_clickable( (By.ID, 'tdFRIENDLYNAME') )
        )
        action_chains = ActionChains(self._browser)
        action_chains.double_click(customer_selection).perform()

        self.__switch_from_csn_address_book_window()

    def __switch_to_notes_frame(self):
        '''
        Switches the ChromeDriver focus to the frame with the form to add notes
        to the CSN claim.
        '''
        while True:
            try:
                WebDriverWait(self._browser, 1).until(
                    EC.frame_to_be_available_and_switch_to_it(
                        (By.ID, 'areaNotesGSFrame')
                    )
                )
                break
            except TimeoutException:
                continue

    def __switch_to_notes_tab(self):
        '''Clicks on the claim's "Notes" tab.'''
        self._browser.switch_to.default_content()

        notes_tab = self._wait.until(
            EC.element_to_be_clickable(
                (By.ID, "navNotes")
            )
        )
        try:
            notes_tab.click()
        except ElementClickInterceptedException:
            self._browser.execute_script(
                "arguments[0].click();", notes_tab
            )

        self.__switch_to_notes_frame()

    def __fill_in_notes(self):
        '''Fills in all the claim's notes.'''
        self.__switch_to_notes_tab()

        notes_field = self._wait.until(
            EC.visibility_of_element_located( (By.ID, 'noteDetails') )
        )

        add_notes_btn = self._wait.until(
            EC.element_to_be_clickable(
                (
                    By.XPATH,
                    '/html/body/table/tbody/tr/td/div/form/'
                    'table[2]/tbody/tr[4]/td[2]/input'
                )
            )
        )

        for note in self._info['notes']:
            notes_field.send_keys(note)
            add_notes_btn.click()

    def __check_order_validated(self):
        '''
        Checks to make sure that all of the data entered into the claim
        has been validated, before checking the messages for the last time.
        '''
        self._wait.until(
            EC.text_to_be_present_in_element(
                (By.CSS_SELECTOR, '#tdInfo > b'),
                'Order Validated, there are status messages on this event. '
                'Visit the Information / Status tab to review them.'
            )
        )

    def __save_and_validate_order(self):
        '''
        Clicks the "Save and Validate" button in the claim, and then makes
        sure that all data is processed, before checking the messages for the
        last time.
        '''
        self._browser.switch_to.default_content()

        self._wait.until(
            EC.element_to_be_clickable( (By.ID, 'EventToolbar1_hpSave') )
        ).click()

        self.__check_order_validated()

    def __check_order_submitted(self):
        '''
        Checks to see if the claim displays a message that the order has been
        successfully submitted.
        '''
        try:
            return self._wait.until(
                EC.text_to_be_present_in_element(
                    (By.ID, 'tdInfo'),
                    'Order has been successfully submitted'
                )
            )
        except TimeoutException:
            return True

    def __submit_order(self):
        '''Clicks the button to submit the order.'''
        self._browser.switch_to.default_content()
        self.__check_claim_info_loaded()

        self._browser.execute_script(
            "arguments[0].click();",
            self._wait.until(
                EC.element_to_be_clickable(
                    (By.ID, 'EventToolbar1_hpSubmitOrder')
                )
            )
        )

        self.__check_order_submitted()

    def __get_csn_event_num(self):
        '''
        Copies or displays the CSN event number for the user to put
        into the Cherwell ticket.
        '''
        # To add the CSN Event number into the Cherwell ticket
        csn_event_num_str = f"CSN #{self._info['event number']}"

        try:
            pyperclip_copy(csn_event_num_str)
        # To still get the CSN event number, if the computer is locked
        except PyperclipWindowsException:
            sg.popup(
                csn_event_num_str,
                title='Info Message',
                grab_anywhere=True,
                keep_on_top=True
            )
        else:
            sg.popup(
                csn_event_num_str,
                title='Info Message',
                grab_anywhere=True,
                keep_on_top=True
            )

    def __switch_to_claim_view_frame(self):
        '''
        Switches the ChromeDriver focus to the frame with the table containing all
        the claims that haven't been submitted.
        '''
        while True:
            try:
                # This frame has no name or ID
                iframe = self._browser.find_element_by_xpath(
                    '//*[@id="body"]/table[2]/tbody/tr/td[3]/'
                    'table/tbody/tr[2]/td/iframe'
                )
                self._browser.switch_to.frame(iframe)
                self._browser.switch_to.frame('_orders')
                break
            # To make sure that all frames have successfully loaded
            except (NoSuchFrameException, NoSuchElementException):
                self._browser.switch_to.default_content()
                continue

    def __select_claims_not_submitted(self):
        '''
        Selects the filter option for viewing claims that only shows
        the claims that haven't been submitted.
        '''
        self.__switch_to_claim_view_frame()

        view_selector = Select(
            self._wait.until(
                EC.element_to_be_clickable( (By.ID, "SavedQuerySelector") )
            )
        )
        view_selector.select_by_visible_text("Claims Not Submitted")

        self.__check_claim_info_loaded()

    def __switch_to_order_status_frame(self):
        '''
        Switches the ChromeDriver focus to the frame in the claim window
        that states whether or not any parts have shipped.
        '''
        self.__switch_to_sales_order_tab()

        while True:
            try:
                self._browser.switch_to.frame('_ctl0_OrderPartsGridFrame')
                self._browser.switch_to.frame('hpGrid')
                self._browser.switch_to.frame('_grid')
                break
            except NoSuchFrameException:
                self._browser.switch_to.default_content()
                self._browser.switch_to.frame('areaOrderGSFrame')
                continue

    def __check_parts_shipped(self):
        '''
        Checks all the parts in the claim, to see if all of them have shipped.
        '''
        self.__switch_to_order_status_frame()
        self.__check_claim_info_loaded()

        while True:
            try:
                status_elems = self._browser.find_elements_by_css_selector(
                    '#gridBodyTable > tbody > tr > td:nth-child(8)'
                )
                break
            except NoSuchElementException:
                continue

        for status_elem in status_elems:

            if not re.search('Shipped', status_elem.text, flags=re.I):
                return False

        return True

    def __switch_to_claim_step_2_returns_tab(self):
        '''Clicks on the claim's "Claim Step 2 - Returns" tab.'''
        self.__switch_to_claim_step_1_repair_tab()

        returns_tab = self._wait.until(
            EC.element_to_be_clickable(
                (By.ID, "ReturnTab")
            )
        )
        try:
            returns_tab.click()
        except ElementClickInterceptedException:
            self._browser.execute_script(
                "arguments[0].click();", returns_tab
            )

    def __switch_to_return_status_frame(self):
        '''
        Switches the ChromeDriver focus to the frame in the claim window
        that states whether or not any parts need to be returned.
        '''
        self.__switch_to_claim_step_2_returns_tab()
        self.__check_claim_info_loaded()

        while True:
            try:
                self._browser.switch_to.frame('_ctl0_hpGrid')
                self._browser.switch_to.frame('hpGrid')
                self._browser.switch_to.frame('_grid')
                break
            except NoSuchFrameException:
                self._browser.switch_to.default_content()
                self._browser.switch_to.frame('areaReturnGSFrame')
                continue

    def __check_must_return_parts(self):
        '''Checks to see if any of the parts in a claim need to be returned.'''
        self.__switch_to_return_status_frame()

        while True:
            try:
                return_msg_elems = self._browser.find_elements_by_xpath(
                    '//*[@id="gridBodyTable"]/tbody/tr/td[9]'
                )
                break
            except NoSuchElementException:
                continue

        for return_msg_elem in return_msg_elems:

            if re.search('Return part', return_msg_elem.text, flags=re.I):
                return self._browser.find_elements_by_xpath(
                    '//*[@id="gridBodyTable"]/tbody/tr/td[3]'
                )

        return []

    def __check_claim_submitted(self):
        '''
        Checks to see if the claim displays a message that it has been
        successfully submitted.
        '''
        try:
            return self._wait.until(
                EC.text_to_be_present_in_element(
                    (By.ID, 'tdInfo'),
                    'Claim has been submitted and will be processed soon.'
                )
            )
        except TimeoutException:
            return True

    def __submit_claim(self):
        '''Clicks the button to submit the claim.'''
        self._browser.switch_to.default_content()
        self.__check_claim_info_loaded()

        self._browser.execute_script(
            "arguments[0].click();",
            self._wait.until(
                EC.element_to_be_clickable(
                    (By.ID, 'EventToolbar1_hpSubmitClaim')
                )
            )
        )

        self.__check_claim_submitted()

    def __switch_to_claim_window(self):
        '''Switches the ChromeDriver focus to the opened claim window.'''
        self._wait.until( EC.number_of_windows_to_be( (2) ) )
        self._browser.switch_to.window(self._browser.window_handles[1])

        self.__check_claim_info_loaded()

    def __switch_from_claim_window(self):
        '''
        Switches the ChromeDriver focus from the opened claim window,
        back to the window with the CSN Service Delivery page.
        '''
        self._browser.close()
        self._wait.until( EC.number_of_windows_to_be( (1) ) )
        self._browser.switch_to.window(self._browser.window_handles[0])

    def __analyze_claim(self, po_elem):
        '''
        Checks if any of the parts in a claim need to be returned,
        or whether or not they have already shipped.

        It submits any claims where none of the parts need to be returned,
        and all of the parts have shipped.
        '''
        # To open the claim window, and switch to it
        self._browser.execute_script("arguments[0].click();", po_elem)
        self.__switch_to_claim_window()

        # To check if any parts need to be returned, and get a list if they do
        #parts_to_return = self.__check_must_return_parts()

        if self.__check_parts_shipped():
            self.__submit_claim()

        # To get back to the page with the claims
        self.__switch_from_claim_window()
        self.__switch_to_all_claims_frame()

    def __switch_to_all_claims_frame(self):
        '''
        Switches the ChromeDriver focus to the frame in the main CSN
        Service Delivery window where all of the claims are listed.
        '''
        while True:
            try:
                self._browser.switch_to.default_content()
                # This frame has no name or ID
                iframe = self._browser.find_element_by_xpath(
                    '//*[@id="body"]/table[2]/tbody/tr/td[3]'
                    '/table/tbody/tr[2]/td/iframe'
                )
                self._browser.switch_to.frame(iframe)
                self._browser.switch_to.frame('_orders')
                self._browser.switch_to.frame('hpGrid')
                self._browser.switch_to.frame('_grid')
                break
            except:
                continue

    def __switch_to_next_page_button_frame(self):
        '''
        Switches the ChromeDriver focus to the frame in the main
        CSN Service Delivery page that has the button to move to
        the next page of claims.
        '''
        while True:
            try:
                self._browser.switch_to.default_content()
                # This frame has no name or ID
                iframe = self._browser.find_element_by_xpath(
                    '//*[@id="body"]/table[2]/tbody/tr/td[3]'
                    '/table/tbody/tr[2]/td/iframe'
                )
                self._browser.switch_to.frame(iframe)
                self._browser.switch_to.frame('_orders')
                break
            except NoSuchFrameException:
                continue

    def __go_to_next_page_of_claims(self):
        '''
        Clicks on the button to go to the next page of claims that
        haven't yet been submitted.
        '''
        self.__switch_to_next_page_button_frame()

        next_page_btn = self._wait.until(
            EC.presence_of_element_located( (By.ID, "_nextPageImg") )
        )

        # To know which page is the last page
        src = next_page_btn.get_attribute('src')

        # This image is used for the button on the last page, but not others
        if not re.search('page_R0.png', src):
            next_page_btn.click()
        else:
            self._browser.quit()
            sg.popup(
                'All applicable claims have been submitted.',
                title='Claim Submission Status'
            )
            self.__is_last_page = True

    def __process_all_ccs_claims_on_page(self):
        '''
        Checks all claims that haven't been submitted on a given page,
        and submits those where none of the parts need to be returned,
        and all of the parts have been shipped.
        '''
        self.__switch_to_all_claims_frame()

        # To figure out how many claims are on a given page
        try:
            claim_elems = WebDriverWait(self._browser, 30).until(
                EC.visibility_of_all_elements_located(
                    (By.CSS_SELECTOR, '#gridBodyTable > tbody > tr')
                )
            )
        except TimeoutException:
            self._browser.quit()
            sg.popup("There are no claims that haven't been submitted.", title='Info Message')

        for i in range( len(claim_elems) ):
            try:
                # To open each claim's window
                po_elem = self._browser.find_element_by_xpath(
                    f'//*[@id="gridBodyTable"]/tbody/tr[{i + 1}]'
                )

                # To get claim's PO number
                po_num = self._browser.find_element_by_xpath(
                    f'//*[@id="gridBodyTable"]/tbody/tr[{i + 1}]/td[7]'
                ).text

                # To check whether or not the order has been submitted
                order_status = self._browser.find_element_by_xpath(
                    f'//*[@id="gridBodyTable"]/tbody/tr[{i + 1}]/td[10]'
                ).text
            except NoSuchElementException:
                continue

            # To only process CCS claims where the order has been submitted
            if re.search('CCS', po_num, flags=re.I) \
            and re.search('Order submitted', order_status, flags=re.I):
                self.__analyze_claim(po_elem)
            else:
                continue

        self.__go_to_next_page_of_claims()

    def create(self):
        self._log_into_hp_warranty_site()
        self.__go_to_service_delivery_page()
        self.__open_new_claim_window()
        self.__set_csn_event_num()
        self.__fill_in_device_and_learner_info()
        self.__check_device_warranty()
        self.__check_status_messages()
        self.__add_parts_to_order()
        self.__fill_in_shipping_info()
        self.__fill_in_event_info()
        self.__fill_in_end_user_info()
        self.__fill_in_notes()
        self.__save_and_validate_order()
        self.__check_status_messages()
        self.__submit_order()
        self._browser.quit()
        self.__get_csn_event_num()

    def submit(self):
        '''
        Submits all Columbus City Schools' CSN claims where all of the parts have shipped.
        '''
        self._log_into_hp_warranty_site()
        self.__go_to_service_delivery_page()
        self.__select_claims_not_submitted()
        self.__check_claim_info_loaded()

        while self.__is_last_page is False:
            self.__process_all_ccs_claims_on_page()

        self._browser.quit()
